﻿using CoreCommon;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OracleClient;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows.Forms;

namespace CoreOracle
{

    /// <summary>
    /// Класс предоставляющий доступ к базе данных Oracle. Синглетон. 
    /// </summary>
    public sealed class DB : IDisposable
    {
        #region Constructors

        /// <summary>
        /// Метод возвращает экземпляр класса
        /// </summary>
        public static DB Item
        {
            get
            {
                if (_db == null)
                {
                    lock (_syncRoot)
                    {
                        if (_db == null)
                            _db = new DB();
                    }
                }
                return _db;
            }
        }

        private DB() { }

    #endregion

        #region Methods

        /// <summary>
        /// Инициализируется соединение с базой данных
        /// </summary>
        /// <param name="dataSource">База данных Oracle</param>
        /// <param name="connectedUser">Имя пользователя</param>
        /// <param name="password">Пароль пользователя</param>
        public void Connect(string dataSource, string connectedUser, string password)
        {
            OracleConnectionStringBuilder builder = new OracleConnectionStringBuilder();
            builder.DataSource = dataSource;
            builder.UserID = connectedUser;
            builder.Password = password;
            if (this.IsActive)
                this.Close();
            else
            {
                _connect = new OracleConnection(builder.ConnectionString);
                _connect.Open();
            }
        }

        /// <summary>
        /// Закрывается соедниение с базой данных
        /// </summary>
        public void Close()
        {
            if (_connect != null)
                _connect.Close();
        }

        /// <summary>
        /// Запускается новая транзакия
        /// </summary>
        public void StartTransaction()
        {
            if (_connect == null)
                throw new CoreException(ErrType.Assert, "WfOracle.Db._connection is null");

            if (_trans != null)
                throw new CoreException(ErrType.Assert, ErrorMsg.ConnectInTransaction);
            _trans = _connect.BeginTransaction();
        }

        /// <summary>
        /// Подтверждение транзакции
        /// </summary>
        public void Commit()
        {
            if (_trans == null)
                throw new CoreException(ErrType.Assert, "WfOracle.Db._trans is null");

            _trans.Commit();
            _trans.Dispose();
            _trans = null;
        }
        
        /// <summary>
        /// Откат транзакции
        /// </summary>
        public void Rollback()
        {
            if (_trans == null)
                throw new CoreException(ErrType.Assert, "WfOracle.Db._trans is null");

            _trans.Rollback();
            _trans.Dispose();
            _trans = null;
        }

        /// <summary>
        /// Добавление входного параметра
        /// </summary>
        /// <param name="name">Имя параметра</param>
        /// <param name="oracleType">Тип параметра</param>
        /// <param name="value">Значение параметра</param>
        /// <param name="command">Экземпляр класса OracleCommand</param>
        /// <param name="IsNull">Флаг, указывающий, может ли параметр принимать значение null</param>
        public static void AddInParameter(string name, OracleType oracleType, object value, OracleCommand command, bool IsNull)
        {
            OracleParameter prm = new OracleParameter(name, oracleType);
            prm.IsNullable = IsNull;
            if (!IsNull && value == null)
                throw new CoreException(ErrType.Assert, ErrorMsg.IsNullParam);
            if (IsNull && value == null)
                prm.Value = DBNull.Value;
            else
                prm.Value = value;
            command.Parameters.Add(prm);
        }

        /// <summary>
        /// Добавляется выходной параметр
        /// </summary>
        /// <param name="name">Имя параметра</param>
        /// <param name="oracleType">Тип параметра</param>
        /// <param name="command">Экземпляр класса OracleCommand</param>
        /// <returns>Экземпляр класса OracleParameter</returns>
        public static OracleParameter AddOutParameter(string name, OracleType oracleType, OracleCommand command)
        {
            OracleParameter prm = new OracleParameter(name, oracleType);
            prm.Direction = ParameterDirection.Output;
            command.Parameters.Add(prm);
            return prm;
        }

        #endregion

        #region Свойства

        /// <summary>
        /// Возвращается текущая коннекция
        /// </summary>
        public OracleConnection Connection
        {
            get { return _connect; }
        }

        /// <summary>
        /// Возвращается текущая транзакция
        /// </summary>
        public OracleTransaction Transaction
        {
            get { return _trans; }
        }

        /// <summary>
        /// Возвращается состояние соединения с базой данных экземпляра
        /// </summary>
        /// <returns>Ture - если соединение активно, false - в обратном случае</returns>
        public bool IsActive
        {
            get { return (_connect != null) && (_connect.State != System.Data.ConnectionState.Closed); }
        }

        /// <summary>
        /// Проверяется состояние текущей транзакции
        /// </summary>
        /// <returns>True - если транзакция запущена, false - в обратном случае</returns>
        public bool InTransaction
        {
            get
            {
                if (_trans == null)
                    throw new CoreException(ErrType.Assert, "WfOracle.Db._trans is null");

                return _trans != null;
            }
        }

        #endregion

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                _connect.Dispose();
            }
        }
        
        /// <summary>
        /// Освобождение ресурса соединения с базой
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        #region Fields

        private static volatile DB _db;
        private static object _syncRoot = new Object();
        private OracleTransaction _trans;
        private OracleConnection _connect;

        #endregion
    }

    /// <summary>
    /// Простейшие методы обращения к базе данных
    /// </summary>
    public sealed class OraWork
    {
        /// <summary>
        /// Выполняется запрос к базе данных без параметров и заполняется переданных DataTable
        /// </summary>
        /// <param name="sql">Текст запроса</param>
        /// <param name="dataTable">DataTable для заполнения</param>
        /// <param name="id">Уникальный ключ</param>
        public static void SqlSimple(string sql, DataTable dataTable, string id)
        {
            OracleCommand cmd = new OracleCommand(sql, DB.Item.Connection);
            OracleDataAdapter da = new OracleDataAdapter(cmd);
            dataTable.Clear();
            da.Fill(dataTable);
            dataTable.PrimaryKey = new DataColumn[] { dataTable.Columns[id] };
        }
        
        /// <summary>
        /// Выполнение запроса для соответствующего уникального идентификатора
        /// </summary>
        /// <param name="sql">Текст запроса</param>
        /// <param name="dataTable">DataTable для заполнения</param>
        /// <param name="id">Уникальный идентификатор - ключ для запроса</param>
        /// <param name="value">Значение идентификатора</param>
        public static void OracleSqlById(string sql, DataTable dataTable, string id, int value)
        {
            OracleCommand cmd = new OracleCommand(sql, DB.Item.Connection);
            DB.AddInParameter(id, OracleType.Number, value, cmd, false);
            OracleDataAdapter da = new OracleDataAdapter(cmd);
            dataTable.Clear();
            da.Fill(dataTable);
            dataTable.PrimaryKey = new DataColumn[] { dataTable.Columns[id] };
        }
        
        /// <summary>
        /// Удаление строки из базы по идентификатору
        /// </summary>
        /// <param name="sql">Текст запроса</param>
        /// <param name="ids">Уникальные идентификаторы</param>
        /// <param name="id">Ключевое поле, по которому проводится удаление</param>
        public static void OracleDeleteSimple(string sql, int[] ids, string id)
        {
            OracleCommand cmd = new OracleCommand(sql, DB.Item.Connection, DB.Item.Transaction);
            cmd.CommandType = CommandType.StoredProcedure;

            foreach (int i in ids)
            {
                cmd.Parameters.Clear();
                DB.AddInParameter(id, OracleType.Number, i, cmd, false);
                cmd.ExecuteNonQuery();
            }
        }

        /// <summary>
        /// В зависимости от методов аутентификации каждого подключаемого приложения возвращает соответствующую реалзиацию методов класса LogOnBase.
        /// Методы реализуются в вызывающем приложении. Для этого в вызывающем приложении должен быть реализован класс, наследник LogOnBase.
        /// Вызов нужной реализации методов использует рефлексию, через динамическую загрузку ассамблеи вызывающей программы и поиск класса LogOnOracle.
        /// </summary>
        public static LogOnBase LogOnBase()
        {
            try
            {
                Assembly ass = Assembly.Load(Assembly.GetEntryAssembly().GetName().Name);
                Type logonType = ass.GetTypes().Where(n => n.Name == "LogOnOracle").ElementAt(0);
                return (LogOnBase)Activator.CreateInstance(logonType);
            }
            catch
            {
                throw new CoreCommon.CoreException(CoreCommon.ErrType.Critical, ErrorMsg.UnloadAssemble);
            }
        }

    }

}
